package org.ascollada.core
{
	import org.ascollada.core.ns.collada;
	import org.ascollada.utils.DaeLogger;
	
	public class DaeChannel extends DaeElement
	{
		use namespace collada;
		
		public static const MEMBER_ACCESS :uint = 0;
		public static const ARRAY_ACCESS :uint = 1;
		
		public var sampler :DaeSampler;
		public var type :int = -1;
		public var source :String;
		public var targetID :String;
		public var targetSID :String;
		public var targetMember :String;
		public var arrayIndex0 :int;
		public var arrayIndex1 :int;
		
		/**
		 * 
		 */ 
		public function DaeChannel(document:DaeDocument, sampler:DaeSampler, element:XML=null)
		{
			super(document, element);
			
			this.sampler = sampler;
		}

		/**
		 * 
		 */ 
		public function getFrameByTime(time:Number):int
		{
			if(!sampler || !sampler.input || !sampler.input.length)
				return -1;
				
			var first :int = 0;
			var last :int = sampler.input.length - 1;
			var current :int = 0;
			
			if(time <= sampler.input[first])
				return first;
				
			if(time >= sampler.input[last])
				return last;
			
			for(current = 0; current < sampler.input.length - 1; current++)
			{
				if(parseFloat(sampler.input[current+1]) > time)
					return current;
			}	
			
			return -1;
		}
		
		/**
		 * 
		 */ 
		public function getOutputAtTime(time:Number, interpolation:String="LINEAR"):Array
		{
			if(!sampler || !sampler.input || !sampler.input.length)
			{
				trace("no sampler, or no input");
				return null;
			}	
			var frame :int = getFrameByTime(time);
			
			if(frame < 0)
			{
				trace("frame < 0");
				return null;
			}
			var st :Number = sampler.input[frame];
			var v0 :Array = sampler.output[frame] is Array ? sampler.output[frame] : [sampler.output[frame]];
			
			if(interpolation == "STEP" || frame >= sampler.output.length - 1)
				return v0;
		
			var et :Number = sampler.input[frame+1];
			var v1 :Array = sampler.output[frame+1] is Array ? sampler.output[frame+1] : [sampler.output[frame+1]];
			var duration :Number = et - st;
			var alpha :Number = (time - st) / duration;
			var output :Array = null;
			var i :int;
			
			if(time <= st || time >= et) alpha = 0;
			
			switch(interpolation)
			{
				case "BEZIER":
				case "HERMITE":
				case "LINEAR":
				default:
					output = new Array(v0.length)
					for(i = 0; i < v0.length; i++)
					{
						output[i] = v0[i] + (alpha * (v1[i] - v0[i]));
					}
					break;
			}
			
			return output;
		}
		
		/**
		 * 
		 */ 
		public override function read(element:XML):void
		{
			super.read(element);
			
			this.source = readAttribute(element, "source", true);
			
			var parts :Array = element.@target.split("/");
				
			var target :String = parts.pop() as String;
			
			if(parseArraySyntax(target))
			{
				this.type = ARRAY_ACCESS;
			}
			else if(parseDotSyntax(target))
			{
				this.type = MEMBER_ACCESS;
			}
			else if(target.length && parts.length)
			{
				this.targetSID = target;
				this.type = MEMBER_ACCESS;
			}
			else
			{
				DaeLogger.log("[ERROR while parsing DaeChannel] " + target);
				return;
			}
			
			if(parts.length)
			{
				this.targetID = parts.shift() as String;
			}
			else
			{
				this.targetID = this.targetSID;
				this.targetSID = null;
			}
		}
		
		private function parseArraySyntax(target:String):Boolean
		{	
			this.arrayIndex0 = -1;
			this.arrayIndex1 = -1;
			
			if(target.indexOf("(") != -1)
			{
				var pattern :RegExp = /(.+)\((\d+)\)\((\d+)\)/g;
				
				var matches:Array = pattern.exec(target);
				
				if(!matches)
				{
					pattern = /(.+)\((\d+)\)/g;
					matches = pattern.exec(target);
				}
				
				if(matches && matches.length > 2) 
				{
					this.targetSID = matches[1];
					this.arrayIndex0 = parseInt(matches[2], 10);
					if(matches.length > 3)
						this.arrayIndex1 = parseInt(matches[3], 10);
					return true;
				}
				else
				{
					DaeLogger.log("[WARNING] channel target contains '(...)', but failed to extract values!");
				}
			}
			
			return false;
		}
		
		private function parseDotSyntax(target:String):Boolean
		{
			if(target.indexOf(".") != -1)
			{
				var parts :Array = target.split(".");
				
				if(parts.length < 2)
				{
					return false;
				}
				this.targetSID = parts.shift() as String;
				this.targetMember = parts.shift() as String;
				return true;
			}
			return false;
		}
		
		public function toString():String
		{
			var str :String = "[DaeChannel ";
			if(this.type == MEMBER_ACCESS)
			{
				str += " (member access) targetID: '" + targetID + "' targetSID: '" + targetSID + "' targetMember: '" + targetMember +"'";
			}
			else
			{
				str += " (array access) targetID: '" + targetID + "' targetSID: '" + targetSID + "' idx0: " + arrayIndex0;
				if(arrayIndex1 >= 0)
					str += " idx1: " + arrayIndex1;
			}
			str += "]";
			return str;
		}
	}
}